#ifndef GAMEENGINE_CORE_DATA_ELEMENT_H
#define GAMEENGINE_CORE_DATA_ELEMENT_H

#include <GameEngine/Core/core_settings.h>
#include <GameEngine/_defs.h>

#include <GameEngine/Core/Data/data_type.h>

#include <QtCore/QList>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

template <class T> class DataItem;


//=================================================================================


//! Abstract interface for the definition of a data element
/*!
 *  This interface specifies some common read/write
 *  operations, plus an access to the number of bits
 *  required for the data storage. These functions
 *  should be implemented in derived classes.
 *
 *  If polymorphism is not required (i.e. that you
 *  know at compilation time what types you require),
 *  it is recommanded to use the DataItem class rather
 *  than the DataElementBase class:<br>
 *  1. You will be able to perform read/write operations
 *     with different types of readers or writers on the
 *     same element<br>
 *  2. The elements will be read or written faster, as
 *     calls to the read/write functions will not be
 *     redirected by a virtual table
 *
 *  If data type is only known at run-time (e.g. it
 *  depends on the content of a file header), then
 *  the use of the DataElementBase class is mandatory
 *
 @todo Setup unit tests
 */
template <class R, class W>
class DataElementBase {

public:
	DataElementBase();
	virtual ~DataElementBase();

	//! Accessor to the number of bits associated to the data element
	virtual int bits() const = 0;
	virtual DataType data_type() const;

	//! Method used to read the element
	virtual bool read_element(R& reader) = 0;
	//! Method used to write the element
	virtual bool write_element(W& writer) const = 0;
};

//=================================================================================

//! Implementation of the DataElementBase class on a specific type of element
/*!
 *  This class provides the implementation of the
 *  read-write functions on an element. See the
 *  DataElementBase class for more details.
 *
 @todo Setup unit tests
 */
template <class T, class R, class W>
class DataElement : public DataElementBase<R,W> {

public:
	DataElement();
	DataElement(const DataItem<T>& item);
	DataElement(const DataElement& rhs);
	virtual ~DataElement();
	DataElement& operator=(const DataElement& rhs);

	T& element();
	const T& element() const;
	template <class C> void convert_to(C& converted) const;

	virtual int bits() const;
	virtual DataType data_type() const;

	virtual bool read_element(R& reader);
	virtual bool write_element(W& writer) const;

private:
	//! Element associated to the data element
	T element_;
};


//=================================================================================

//! Implementation of the DataElementBase class for a list of elements
/*!
 *  This class provides the implementation of the
 *  read-write functions on a list of elements. See
 *  the DataElementBase class for more details.
 *
 @todo Setup unit tests
 */
template <class R, class W>
class DataElementComposite : public DataElementBase<R,W> {

public:
	DataElementComposite();
	DataElementComposite(const DataElementComposite& rhs);
	virtual ~DataElementComposite();
	DataElementComposite& operator=(const DataElementComposite& rhs);

	template <class T> bool add_element(const DataElement<T,R,W>& element);
	bool add_element(DataType type);
	const QList<DataElementBase<R,W>*>& elements() const;

	virtual int bits() const;
	virtual DataType data_type() const;

	virtual bool read_element(R& reader);
	virtual bool write_element(W& writer) const;

protected:
	//! List of elements associated to the composite data element
	QList<DataElementBase<R,W>*> elements_;
};


//=================================================================================


//! A data item is an element with read/write functions
/*!
 @note An instance of a data item can be read or written
       using different types of readers/writers. However,
	   its read/write functions cannot be virtual, because
	   there would potentially be an infinity of virtual
	   instanciations of these functions to add to the
	   virtual table.<br>
	   If you need to be able to detect data types at run-time,
	   depending on a given context (e.g. the header of a
	   file), please use the DataElementBase class. An instance
	   of a data element can only use one type of reader and
	   one type of writer, but it is allowed to use virtual
	   functions.
 *
 @todo Setup unit tests
 */
template <class T>
class DataItem {

public:
	DataItem();
	DataItem(const DataItem& rhs);
	template<class R, class W> DataItem(const DataElement<T,R,W>& element);
	virtual ~DataItem();
	DataItem& operator=(const DataItem& rhs);

	T& element();
	const T& element() const;
	template <class C> void convert_to(C& converted) const;

	int bits() const;
	DataType data_type() const;

	template <class R> bool read_element(R& reader);
	template <class W> bool write_element(W& writer) const;

private:
	//! Element associated to the data item
	T element_;
};

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif

#include <GameEngine/Core/Data/data_element.hpp>

#endif